
#include "messager.h"

// ##
#define LOG_TAG    "messager"
#include "log.h"

// ##
bool is_handle_mode(void);

// ##
bool is_handle_mode(void)
{
#ifndef SIMULATOR
    return (__get_IPSR() != 0);
#else
    return false;
#endif
}

// ##
typedef union
{
    uint8_t *buffer;
    uint32_t value;

}mParam_t;

typedef struct
{
    uint32_t msg_id;
    mParam_t payload;

}mPack_t;

struct queueLinkNode
{
    QueueHandle_t queue;

    struct queueLinkNode *next_queue;
};

struct queueList_t
{
    struct queueLinkNode *node;
};

// ##
#define MESSAGE_IS_VALUE     (1 << 30)
#define MESSAGE_IS_POINTER   (0 << 30)


// ##
static SemaphoreHandle_t msg_mutex_id = NULL;

// ##
static struct queueList_t queuelist[MSG_END];

mResult_t messager_init(void)
{
    // ##
    memset(queuelist, 0x00, sizeof(queuelist));

    // ##
    msg_mutex_id = xSemaphoreCreateMutex();

    if(msg_mutex_id == NULL)
    {
        log_error("failed to create mutex.\r\n");

        return RESULT_ERR;
    }
    else
    {
        return RESULT_OK;
    }
}

// ##
mMessager_t messager_create(uint8_t depth)
{
    mMessager_t messager = NULL;

    if(!depth)
    {
        log_error("depth should not be zero.\r\n");

    }
    else
    {
        messager = xQueueCreate(depth, sizeof(mPack_t));

        if(!messager)
        {
            log_error("failed to create queue.\r\n");

        }
    }

    return messager;
}

// ##
static mResult_t messager_insert(struct queueList_t *list, mMessager_t queue)
{
    uint8_t exist = 0;

    struct queueLinkNode *cur_node = NULL;
    struct queueLinkNode *new_node = NULL;

    if(NULL == list || queue == NULL)
    {
        return RESULT_ERR;
    }

    cur_node = list->node;

    while(NULL != cur_node)
    {
        new_node = cur_node;

        if(queue == cur_node->queue)
        {
            exist = 1;

            break;
        }

        cur_node = cur_node->next_queue;
    }

    if(!exist)
    {
        cur_node = (struct queueLinkNode *)pvPortMalloc(sizeof(struct queueLinkNode));

        if(NULL == cur_node)
        {
            return RESULT_ERR;
        }

        cur_node->queue = queue;
        cur_node->next_queue = NULL;

        new_node->next_queue = cur_node;
    }

    return RESULT_OK;
}

// ##
static mResult_t messager_delete(struct queueList_t *list, mMessager_t queue)
{
    uint8_t exist = 0;

    struct queueLinkNode *cur_node = NULL;
    struct queueLinkNode *prv_node = NULL;

    if(NULL == list || NULL == queue)
    {
        return RESULT_ERR;
    }
    else
    {
        cur_node = list->node;

        while(NULL != cur_node)
        {
            if(queue == cur_node->queue)
            {
                exist = 1;

                break;
            }

            prv_node = cur_node;

            cur_node = cur_node->next_queue;
        }

        if(exist)
        {
            if(cur_node == list->node)
            {
                list->node = cur_node->next_queue;
            }
            else
            {
                prv_node->next_queue = cur_node->next_queue;
            }

            vPortFree(cur_node);

            return RESULT_OK;
        }
        else
        {
            return RESULT_ERR;
        }
    }
}

// ##
mResult_t messager_bind(msg_t msg, mMessager_t queue)
{
    struct queueLinkNode *new_node;

    if(queue == NULL || msg_mutex_id == NULL)
    {
        return RESULT_ERR;
    }
    else
    {
        if(msg < MSG_END)
        {
            if(xSemaphoreTake(msg_mutex_id, portMAX_DELAY) == pdTRUE)
            {
                if(NULL == queuelist[msg].node)
                {
                    new_node = (struct queueLinkNode *)pvPortMalloc(sizeof(struct queueLinkNode));

                    if(NULL == new_node)
                    {
                        xSemaphoreGive(msg_mutex_id);

                        return RESULT_ERR;
                    }
                    else
                    {
                        new_node->queue = queue;
                        new_node->next_queue = NULL;

                        queuelist[msg].node = new_node;

                        xSemaphoreGive(msg_mutex_id);

                        return RESULT_OK;
                    }
                }
                else
                {
                    mResult_t result = messager_insert(&queuelist[msg], queue);

                    xSemaphoreGive(msg_mutex_id);

                    return result;
                }
            }
            else
            {
                return RESULT_ERR;
            }
        }
        else
        {
            return RESULT_ERR;
        }
    }
}

// ##
mResult_t messager_unbind(msg_t msg, mMessager_t queue)
{
    if(queue == NULL || msg_mutex_id == NULL)
    {
        return RESULT_ERR;
    }
    else
    {
        if(xSemaphoreTake(msg_mutex_id, portMAX_DELAY) == pdTRUE)
        {
            mResult_t result = messager_delete(&queuelist[msg], queue);

            xSemaphoreGive(msg_mutex_id);

            return result;
        }
        else
        {
            return RESULT_ERR;
        }
    }
}

// ##
mResult_t messager_post(msg_t msg, void *content, uint32_t content_size, uint32_t flag)
{
    // ##
    mPack_t package;
    struct queueLinkNode *node_root = NULL;

    if(is_handle_mode() == true)
    {
        log_error("messager_post can't be called in handle mode, msg[%d] error : line %d.\r\n", msg, __LINE__);
        return RESULT_ERR;
    }

    if(msg > MSG_END)
    {
        log_error("messager_post msg[%d] error : line %d.\r\n", msg, __LINE__);
        return RESULT_ERR;
    }

    node_root = queuelist[msg].node;

    while((NULL != node_root) && (NULL != node_root->queue))
    {
        if(content_size > MESSAGE_MAX_LENGTH)
        {
            log_error("messager_post msg[%d] error : line %d.\r\n", msg, __LINE__);
            return RESULT_ERR;
        }

        if(MESSAGE_IS_POINTER == flag)
        {
            package.payload.buffer = pvPortMalloc(content_size + 4);

            if(NULL == package.payload.buffer)
            {
                log_error("messager_post msg[%d] error : line %d.\r\n", msg, __LINE__);
                return RESULT_ERR;
            }

            *(uint32_t *)package.payload.buffer = content_size;

            memcpy((uint8_t *)package.payload.buffer + 4, (uint8_t *)content, content_size);

            package.msg_id = msg | MESSAGE_IS_POINTER;
        }
        else
        {
            package.payload.value = *((uint32_t *)content);

            package.msg_id = msg | MESSAGE_IS_VALUE;
        }

        if(xQueueSend(node_root->queue, &package, portMAX_DELAY) != pdPASS)
        {
            log_error("messager_post msg[%d] error : line %d.\r\n", msg, __LINE__);
            return RESULT_ERR;
        }

        node_root = node_root->next_queue;
    }

    return RESULT_OK;
}

// ##
mResult_t messager_notify(msg_t msg, uint32_t notification)
{
    return messager_post(msg, &notification, sizeof(uint32_t), MESSAGE_IS_VALUE);
}

// ##
mResult_t messager_mail(msg_t msg, void *content, uint32_t size)
{
    return messager_post(msg, content, size, MESSAGE_IS_POINTER);
}

// ##
mResult_t messager_recv(mMessager_t queue, msg_t *msg, void *content, uint32_t *content_size, uint32_t buffer_size)
{
    mPack_t package;

    if(queue == NULL)
    {
        log_error("messager_recv msg[%d] error : line %d.\r\n", *msg, __LINE__);
        return RESULT_ERR;
    }

    if(pdFALSE == xQueueReceive(queue, &package, portMAX_DELAY))
    {
        log_error("messager_recv msg[%d] error : line %d.\r\n", *msg, __LINE__);

        return RESULT_ERR;
    }

    if(MESSAGE_IS_VALUE == (package.msg_id & MESSAGE_IS_VALUE))
    {
        *msg = (msg_t)(package.msg_id - MESSAGE_IS_VALUE);
        *content_size = sizeof(uint32_t);
        *(uint32_t *)content = package.payload.value;

        return RESULT_OK;
    }
    else
    {
        *msg = (msg_t)package.msg_id;

        if(NULL != package.payload.buffer)
        {
            if(*(uint32_t *)package.payload.buffer <= MESSAGE_MAX_LENGTH)
            {
                *content_size = *(uint32_t *)package.payload.buffer;

                if (*(uint32_t *)package.payload.buffer <= buffer_size)
                {
                    memcpy((uint8_t *)content, (uint8_t *)package.payload.buffer + 4, *(uint32_t *)package.payload.buffer);
                }
                else
                {
                    memcpy((uint8_t *)content, (uint8_t *)package.payload.buffer + 4, buffer_size);
                }

                vPortFree(package.payload.buffer);
                return RESULT_OK;
            }
            else
            {
                vPortFree(package.payload.buffer);
                log_error("messager_recv msg[%d] error : line %d.\r\n", *msg, __LINE__);

                return RESULT_ERR;
            }
        }
    }

    log_error("messager_recv msg[%d] error : line %d.\r\n", *msg, __LINE__);

    return RESULT_ERR;
}


// ##
mResult_t messager_recv_timeout(mMessager_t queue, msg_t *msg, void *content, uint32_t *content_size, uint32_t buffer_size,int ms)
{
    mPack_t package;

    if(queue == NULL)
    {
        log_error("messager_recv msg[%d] error : line %d.\r\n", *msg, __LINE__);
        return RESULT_ERR;
    }

    TickType_t delay_time = pdMS_TO_TICKS(ms);

    if(pdFALSE == xQueueReceive(queue, &package, delay_time))
    {
        // log_error("messager_recv msg[%d] error : line %d.\r\n", *msg, __LINE__);

        return RESULT_ERR;
    }

    if(MESSAGE_IS_VALUE == (package.msg_id & MESSAGE_IS_VALUE))
    {
        *msg = (msg_t)(package.msg_id - MESSAGE_IS_VALUE);
        *content_size = sizeof(uint32_t);
        *(uint32_t *)content = package.payload.value;

        return RESULT_OK;
    }
    else
    {
        *msg = (msg_t)package.msg_id;

        if(NULL != package.payload.buffer)
        {
            if(*(uint32_t *)package.payload.buffer <= MESSAGE_MAX_LENGTH)
            {
                *content_size = *(uint32_t *)package.payload.buffer;

                if (*(uint32_t *)package.payload.buffer <= buffer_size)
                {
                    memcpy((uint8_t *)content, (uint8_t *)package.payload.buffer + 4, *(uint32_t *)package.payload.buffer);
                }
                else
                {
                    memcpy((uint8_t *)content, (uint8_t *)package.payload.buffer + 4, buffer_size);
                }

                vPortFree(package.payload.buffer);
                return RESULT_OK;
            }
            else
            {
                vPortFree(package.payload.buffer);
                log_error("messager_recv msg[%d] error : line %d.\r\n", *msg, __LINE__);

                return RESULT_ERR;
            }
        }
    }

    log_error("messager_recv msg[%d] error : line %d.\r\n", *msg, __LINE__);

    return RESULT_ERR;
}

// ##
mResult_t messager_register(mMessager_t mail, uint32_t *mail_list, uint32_t len)
{
    uint32_t i = 0;

    for(i = 0;i < len;i ++)
    {
        if(messager_bind((msg_t)mail_list[i], mail) != RESULT_OK)
        {
            return RESULT_ERR;
        }
    }

    return RESULT_OK;
}

// ##
mResult_t messager_unregister(mMessager_t mail, uint32_t *mail_list, uint32_t len)
{
    uint32_t i = 0;

    for(i = 0;i < len;i ++)
    {
        if(messager_unbind((msg_t)mail_list[i], mail) != RESULT_OK)
        {
            return RESULT_ERR;
        }
    }

    return RESULT_OK;
}
